/**  
* @Title: AuthenticationInterceptor.java
* @Package com.lemon.conf
* @Description: (一句话阐述):
* @author Ran_cc
* @date 2018年5月13日
* @version V1.0  
*/
package com.lemon.conf.MyInterceptor;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.lemon.conf.redis.RedisUtil;
import com.lemon.model.cityRun.CrShopUser;
import com.lemon.service.cityRun.CrShopUserService;
import com.lemon.utils.IpUtils;
import com.lemon.utils.StringUtil;
import com.lemon.utils.TokenUtils;

import io.jsonwebtoken.Claims;
/**
 * <p>Title:拦截器 </p>
 * <p>Description: 包含用户登录校验,使用token令牌校验</p>
 * <p>Company: </p>
 * @author Ran_cc
 * @date 2018年5月13日
 * @版本 V 1.0 
 */
public class AuthenticationInterceptor implements HandlerInterceptor {

	private Logger log = LoggerFactory.getLogger(AuthenticationInterceptor.class);
	
	public final static String ACCESS_TOKEN = "accessToken";
	
	
    /*@Autowired
    private SysUserService userService;*/
	@Autowired
	private CrShopUserService userService;

    /**
     * 在请求处理之前进行调用（Controller方法调用之前）
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    	
		String header = request.getHeader("User-Agent");
		String ip = IpUtils.getIpAddr(request);
		Object object = request.getSession().getAttribute("user");
		CrShopUser tk = null;
		/************* 记录操作日志 ***************/
		//打印操作信息 , 请求参数
		/*
		 * InputStream is = request.getInputStream();
		 * StringBuilder responseStrBuilder = new StringBuilder();
		 * BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
		 * String inputStr;
		 * while ((inputStr = streamReader.readLine()) != null) {
		 * responseStrBuilder.append(inputStr);
		 * }
		 */
		try {
			String paramStr = " ";
			String metName = request.getMethod();
			if ("POST".equals(metName)) {
				//获取请求参数
				Map<String , String[]> map = request.getParameterMap();
				for (Entry<String , String[]> entry : map.entrySet()) {
					paramStr += entry.getKey() + "=" + entry.getValue()[0] + "&";
				}
			} else {
				paramStr += request.getQueryString() == null ? "" : request.getQueryString();
				paramStr += "&";
			}
			if (!"/p/getPrinterOrder".equals(request.getServletPath())) {
				log.info("【请求api:{}】【请求IP:{}】" , request.getServletPath() , ip);
				log.info("【请求参数:{}】" , paramStr.substring(0 , paramStr.length() - 1));
				log.info("【accessToken:{}】" , request.getHeader(ACCESS_TOKEN));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}


		if (object != null) {
			tk = (CrShopUser) object;
			//			log.info("【请求api:{}】【账号:{}】" , request.getServletPath() , tk.getUserAccount());
			//			log.info("【请求IP:{}】【请求参数:{}】" , ip , request.getQueryString());
		} else {
			//			log.info("【请求api:{}】【请求IP:{}】【请求参数:{}】" , request.getServletPath() , ip , request.getQueryString());
		}
		/****************** 记录操作日志 **********************/

		//String accessToken = request.getHeader(ACCESS_TOKEN);
    	/*****************添加当前登陆者所拥有的权限信息********************/
		// TODO 待添加登陆者的权限控制
    	/*****************添加当前登陆者所拥有的权限信息********************/
		/***************** 判断用户注册是否频繁 ********************/
		// TODO 判断用户注册是否频繁
		/***************** 判断用户注册是否频繁 ********************/
    	
    	if (!(handler instanceof HandlerMethod)) {
    		return true;
    	}
    	HandlerMethod handlerMethod = (HandlerMethod) handler;
    	Method method = handlerMethod.getMethod();

    	Claims claims = null;
		/*
		 * if(accessToken!=null){
		 * claims = TokenUtils.parseJWT(accessToken);
		 * if(claims==null){
		 * //设置好过期时间之后, 如果过期了会返回一个空值 . 然后跳转到登陆页面去登陆
		 * redirect(request,response);
		 * return false;
		 * }else{
		 * //生成新的token保存在头里面
		 * String createJwtToken = TokenUtils.createJwtToken(claims.getId());
		 * response.setHeader("accessToken", createJwtToken);
		 * }
		 * String tokenId = claims.getId();
		 * //【】
		 * log.info("执行 Api:\"{}\" - 用户:\"{}\"  - IP:\"{}\"",reqUrl,tokenId,ip);
		 * }else{
		 * log.info("UnKnow 执行 Api:\"{}\" - IP:\"{}\"",reqUrl,ip);
		 * }
		 */

        // 如果不是映射到方法直接通过
        // 判断接口是否需要登录
        LoginRequired methodAnnotation = method.getAnnotation(LoginRequired.class);
        // 有 @LoginRequired 注解，需要认证
        if (methodAnnotation != null) {
            // 判断是否存在令牌信息，如果存在，则允许登录
			String accessToken = request.getHeader(ACCESS_TOKEN);
			//            if (null == accessToken ) {
			if (StringUtil.isEmpty(accessToken) || "null".equals(accessToken)) {
				//				log.info("Token失效 - 【用户编号:{}】" , tk == null ? null : tk.getuId());
				log.info("跳往登陆路径");
				redirect(request , response);
            	return false;
                //throw new RuntimeException("Token令牌失效，请重新登录");
            }
			claims = TokenUtils.parseJWT(accessToken);
			System.out.println("header-token:" + accessToken);
			if (claims == null) {
				//设置好过期时间之后, 如果过期了会返回一个空值 . 然后跳转到登陆页面去登陆
				log.info("跳往登陆路径");
				redirect(request , response);
				return false;
			} else {
				System.out.println("claims-id:" + claims.getId());
				//如果没有过期, 则更新口令为最新的
				String createJwtToken = TokenUtils.createJwtToken(claims.getId());
				response.setHeader("accessToken" , createJwtToken);
			}
			String idInfo = claims.getId(); //获得令牌中加密的值  : 用户的uid:用户编号
			System.out.println("token-uid" + idInfo);

			//将用户信息保存起来
			CrShopUser userInfo = userService.selectShopUserByAcc(idInfo); // 查询用户信息
			if (userInfo == null) {
				RedisUtil.del("tkUserInfo::" + idInfo);
				userInfo = tk;
				if (userInfo == null) {
					// log.info(" 用户信息异常 -【用户编号:{}】" , tk == null ? null : tk.getuId());
					log.info("跳往登陆路径");
					redirect(request , response);
					return false;
				}
				//throw new RuntimeException("用户信息异常，请重新登录");
			} else {
				//判断用户状态
				if (userInfo.getUserStatus() != 1) {
					log.info("用户登录状态异常.跳转到登陆页面");
					redirect(request , response);
					return false;
				}
				tk = userInfo;
				//判断每一次需要登录请求,用户的请求ip是否和最后一次ip相同,不相同需要重新登录
				// TODO 判断用户登录ip 是否在登录记录里面, 如果存在则查出来最近的一条记录 并判断登陆时间和当前时间是否超过7天
				/*
				 * String lastLoginIp = userInfo.getLastLoginIp();
				 * if (StringUtil.isNotBlank(lastLoginIp) && !ip.equals(lastLoginIp)) {
				 * log.info("跳往登陆路径");
				 * redirect(request , response);
				 * return false;
				 * }
				 */
			}

            // 当前登录用户@CurrentUser
			request.setAttribute(CurrentUserConstants.CURRENT_USER , tk);
			request.getSession().setAttribute(CurrentUserConstants.CURRENT_USER , tk);
            return true;
		} else {
			/*
			 * String accessToken = request.getHeader(ACCESS_TOKEN);
			 * response.setHeader("accessToken" , accessToken);
			 */
		}
        return true;
    }

    /**
     * 请求处理之后进行调用，但是在视图被渲染之前（Controller方法调用之后）
     *
     * @param httpServletRequest
     * @param httpServletResponse
     * @param o
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    }

    /**
     * 在整个请求结束之后被调用，也就是在DispatcherServlet 渲染了对应的视图之后执行（主要是用于进行资源清理工作）
     *
     * @param httpServletRequest
     * @param httpServletResponse
     * @param o
     * @param e
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    	
    }
    
    //重定向到登陆页面
    public void redirect(HttpServletRequest request, HttpServletResponse response) throws Exception{
    	//设置好过期时间之后, 如果过期了会返回一个空值 . 然后跳转到登陆页面去登陆
		System.out.println("重定向到登陆页面...");
		response.sendRedirect(request.getContextPath() + "/adm/login.do");
    }
}
